RoR_chapter3 タスク管理アプリケーションをゼロから作ろう
RoR_chapter3 タスク管理アプリケーションをゼロから作ろう
雛形を作る
code:console
// 作りたいディレクトリで、rails new アプリケーション名 オプション
rails new taskleaf -d postgresql
マイグレーションをかける
bin/rails db:create
これをやらないとサーバ起動してもRailsへようこそ的な画面は出ないので注意。
環境でDBを使い分ける
code:txt
# 開発、テスト用の環境2つのデータベースが作られている
bin/rails db:create
Created database 'taskleaf_development'
Created database 'taskleaf_test'
railsの定義する各環境について
development
開発用。開発時の動作確認を行う
test
自動テストを行うための環境。
production
ユーザーが利用可能な形で稼働させることを想定した環境。
slimを使う
railsはデフォルトでerbというテンプレートエンジンが使われている。
ただし現場ではerbだけでなく、slimやhamlといったエンジンが使われることが多い。
実際自分のところもそう
slimを使うために、2つのgem(Rubyのライブラリ)を導入する
slim-rails
slimのジェネレータを提供してくれるgem
html2slim
erb形式のファイルをslim形式に変換してくれるコマンドを提供するgem
code:3_chapter/taskleaf/Gemfile
# 一番下に追記すればよい
gem 'slim-rails'
gem 'html2slim'
記述が終わったら、bundle(もしくは、bundle install)の実行でgemをインストールする。
サーバは再起動する必要があるので気を付ける。
これでコマンドで作成するビューファイルがslim形式になった。
app/views/layoutsにはまだerbファイルが残っているので、これもslimに変更しておこう
bundle exec erb2slim app/views/layouts/ --delete
さっき入れたgemを使って、erbファイルを変換することができた
bootstrapの導入
gem 'bootstrap' → bundleでOK
導入後はbootstrapのcssを反映させる。今回、記述方法はSassでcssを書く。
code:txt
rm assets/stylesheets/application.css //cssを消して
touch app/assets/stylesheets/application.scss //scss形式ファイルを作って
code:application.scss
@import "bootstrap"; // と書いておくと読み込める
→反映されているかをviewファイルをいじってチェックしておく。
エラーメッセージの日本語化
を落とす
code:text
wgetコマンドなかったので取る
brew install wget
持ってこれた。次に日本語がデフォルトになるように設定してみる
3_chapter/taskleaf/config/initializers/locale.rbを作って、デフォルトとなるような構文を記入
モデルを作る
タスク管理機能の実装を行うので、操作対象となるモデルを作成する。
モデルの名前を決めて、そのモデルに対応するクラスとDBテーブルなどを構成する。
モデルのクラス名とDBテーブルの命名規則
テーブル名はモデルのクラス名を複数形としたもの.tasks
クラス名はキャメルケース(大文字続き)、テーブル名はスネークケース Taskモデル
属性を考える
モデルの属性は、DBテーブルのカラムに対応する
なので属性を持たせる=tasksテーブルにどんなカラムを持たせるかを考えるということ
今回はid, name, description, created_at, updated_atを持たせる
文字列の型としてtext,stringがあるが、短い文字列はstringを使う傾向がある
なのでnameはstring, descriptionはtextとして考える
ひな形を作る
ジェネレータを使うと楽
code:rb
bin/rails g model Task name:string description:text
unning via Spring preloader in process 47019
invoke active_record
create db/migrate/20230203134630_create_tasks.rb
create app/models/task.rb
invoke test_unit
create test/models/task_test.rb
create test/fixtures/tasks.yml
それぞれのひな形ファイルが完成する。
モデルのクラスファイル task.rb
Taskクラスの実装に使用する
マイグレーションファイル (DATE)_create_tasks.rb
DBにtasksテーブルを追加するときに使用する
モデルの自動テストファイル task_test.rb
Taskクラスについての自動テストの実装に使用する
自動テストに使うfixtureファイルtasks.yml
テストに使うためのデータ投入の定義が書かれている
マイグレーションする
モデルを作成した場合、DBのマイグレーションから行うとスムーズに作業が進みやすい。
マイグレーションファイルを見て、各点を確認する
changeメソッド
tasksというテーブルを作ること
各指定したカラムを備えること
打刻用のカラムを定義すること(t.timestamps)があることが分かる
必要ならば別途記述する。OKならマイグレートする
rails db:migrate
マイグレートが終われば、クラスファイルをチェック。
code:task.rb
class Task < ApplicationRecord
end
これでも十分な機能を備えている(親からDB操作の機能などを継承しているため)
コントローラとビューを見る
コントローラも作成しよう
bin/rails g controller tasks index show new edit
4つアクションを指定して作っているが、ジェネレータで作ると4つ分のビューも作ってくれる。
さらにルーティングも自動で記述してくれる
code:config/routes.rb
Rails.application.routes.draw do
get 'tasks/index'
get 'tasks/show'
get 'tasks/new'
get 'tasks/edit'
end
こりゃ便利だけど、自分でルーティングも定義してみよう
code:routes.rb
Rails.application.routes.draw do
resources :tasks # 全てのアクションに関するルーティングを一括定義してくれる
root to: 'tasks#index' # ルートパスにアクセスした時のコントローラ処理を指定
end
新規登録機能の実装
流れをまずは考える。
一覧画面的なものに登録ボタンがある→色々入力して登録ボタン→DBに格納される
indexアクション→newアクション→createアクション(ビュー無)→indexビューに遷移
table:実装する内容
機能 アクション 実現すること 画面での実装内容
一覧表示 tasks#index タスクデータの取得、一覧表示 新規登録ボタン
新規登録画面 tasks#new 登録画面の準備 登録フォーム、登録ボタン
登録 tasks#create データを受け取りDBに保存、リダイレクト なし
index
画面にボタンを作る。
= link_to "新規登録", new_task_path, class: 'btn btn-primary'
new_task_pathはヘルパメソッド。これで/tasks/newというURLを取得できる
これを使うためにはroutes.rbに記述をする必要がある
モデルの翻訳情報を入れておく
code:condig/locales/ja.yml
# 追加
models:
task: タスク
attributes:
task:
id: ID
name: 名称
description: 詳しい説明
created_at: 登録日時
updated_at: 更新日時
DRYになる
Don't Repeat Yourself
繰り返しを避けること。
今回はTaskモデルがどういった役割なのかを別の開発者向けに書いておくような行為。
ビュー側でModel.human_attribute_nameというメソッドで呼び出すときにも使える
("一覧表示しよう"を参照)
newでコントローラアクションを実装する
code:rb
def new
@task = Task.new
end
# ビューファイルの指定などない場合は、自動でapp/views/コントローラ/アクション.html.slimを参照する
アクションへデータを送る方法は、GET / POSTなどがある
railsはプログラマが区別しなくて済むように、paramsメソッドでパラメータを取得してくれる
code:rb
# routes.rbに "tasks/:id" と記載していた場合、
# task/7 という値のパラメータを取りたいなら、params:idで取得ができる ビューファイルのレイアウトについて
code:application.html.slim
body
.app-title.navbar.navbar-expand-md.navbar-light.bg-light
.navbar-brand Taskleaf
.container
= yield
# ここがちゃんとインデントされてないと入れ子にならないので気をつける
# (.containerにyieldで読んでくるビューが入らないということ)
form関数とモデルの紐付け方
code:rb
# model: モデル という引数を渡すことで、フォームの内容をどのモデルに入れるかを紐づけられる
= form_with model: @task, local: true do |f|
登録ボタンについて
= f.submit nil, class: 'btn btn-primary'と書いてるだけなのに、「登録する」と入れてくれている
https://scrapbox.io/files/63de597363c733001d137591.png
翻訳情報を見てみる。
code:config/locales/js.yml
submit:
create: 登録する
submit: 保存する
update: 更新する
これはrailsが@taskの状態を見て、オブジェクトに何もないことを確認
新規登録ということで、createかな、という形でrails側が文字を出してくれている すごい
ついでにformのsubmit先のURLも...恐ろしい
createアクションを定義する
code:rb
def create
# 安全に取得したデータからTaskオブジェクトを作成し保存、リダイレクト
task = Task.new(task_params)
task.save!
redirect_to tasks_url, notice: "タスク「#{task.name}」を登録しました。"
end
private
def task_params
# パラメータがtask{...}の形なのかをチェックし、その中からname,descriptionのパラメータを抜き取る
# CSRF対策, 適当なパラメータが送られた場合、その情報で更新されてしまうのを防ぐ.
params.require(:task).permit(:name, :description)
end
Railsの定義する、レンダーとリダイレクト
アクションに続けてビューを表示させることをレンダー
renderメソッドを使う
アクション処理直後にブラウザにリクエストし直すように指示し再びアクションを回すことをリダイレクト
redirect_to. flashメッセージを渡せる.
一覧表示しよう
DBに保存できたようだが、実際画面でまだ見えないので見せよう
code:rb
# DBに保存された全てのタスクデータを取得して、 @tasks というインスタンス変数に格納する
def index
@tasks = Task.all
end
code:slim
.mb-3
table.table.table-hover
thead.thead-default
tr
th= Task.human_attribute_name(:name) # 名称
th= Task.human_attribute_name(:created_at) # 登録日時
tbody
- @tasks.each do |task|
tr
td= task.name
td= task.created_at
Task.human_attribute_name(:name)で呼ばれる名称は、翻訳ファイルに書いた内容となる。
code:ja.yml
attributes:
task:
id: ID
name: 名称
description: 詳しい説明
created_at: 登録日時
updated_at: 更新日時
詳細表示機能を作る
クリックしたら詳細ページに飛ぶようにしよう
td= task.nameをlink_toを使った形にする
ドキュメントを見ると、こんな形
td= link_to task.name, controller: "tasks", action: "show", id: task.id
別の書き方
td= link_to task.name, task
td= link_to task.name, task_path(task)
詳細ページ
一覧ページに戻るためのリンクを作る
= link_to '一覧', tasks_path, class:'nav-link'
task_path(task)は単体系で1つの値が渡されてたのでrailsが詳細ページのことかな?と判断してくれてた
今回はtasks_pathと全体っぽい書き方なので、railsがindex(一覧画面)へのURLを判断して出してくれている
複数行を表示する
code:slim
td= simple_format(h(@task.description), {}, sanitize: false, wrapper_tag: "div");
改行コードが含まれるDBの値を表示させる、simple_formatメソッド
h(): htmlエスケープ
{}: 謎.オプション?
sanitize:XSSに使われるようなhtmlタグを無効化してくれる
"htmlタグ": DBの値にhtmlタグが使われている場合でも、ここで指定したタグで挟んでくれる
編集画面の実装
導線を整理する
indexもしくはshowで編集ボタン→edit→更新コントローラupdate→また戻る
index
td= link_to '編集', edit_task_path(task), class: 'btn btn-primary mr-3'
task/[:id]/editというURLは、edit_model_path(model)で実現できる
show
= link_to '編集', edit_task_path, class: 'btn btn-primary mr-3'
このページで呼び出されているモデルの数は1つ。
edit_task_pathとするだけでそのオブジェクトに対してのURLを作ってくれる
edit, update
code:rb
def edit
# フォーム値に自動で入れるためのものを取っとく
@task = Task.find(params:id) end
def update
# URLを参考にアップデート対象のデータを取って
task = Task.find(params:id) # task_paramsで安全に取得したデータでアップデートをかけ、リダイレクト
task.update!(task_params)
redirect_to tasks_url, notice: "タスク「#{task.name}」を更新しました。"
end
パーシャルを使う
newとeditの画面がほぼ一緒なので共通化してしまおう
renderメソッドのパーシャルオプションを使う
パーシャルとして使う場合、ファイルの先頭はアンダースコア_にする。
今回は_form.html.slimというファイルで行う。
code:html.slim
# 呼び出す側
= render partial: 'form', locals: {task: @task}
# 呼び出される側.パーシャル.(_form.html.slim)
# taskという変数の中に"@task"が入っている
= form_with model: task, local: true do |f|
.form-group
= f.label :name
= f.text_field :name, class: 'form-control', id: 'task_name'
.form-group
= f.label :description
= f.text_area :description, rows: 5, class: 'form-control', id: 'task_description'
= f.submit nil, class: 'btn btn-primary'
削除機能を作る
indexorshowに削除ボタン→destroyアクション→indexに遷移するイメージで
code:html.slim
= link_to '削除', task, method: :delete, data: {confirm: "タスク「#{task.name}」を削除します。よろしいですか?"}, class: 'btn btn-danger'
各種メソッド
削除アクションへのリンクmethod: :deleteという記載をするk指定がある
これを忘れると、taskとしか遷移URLを書いていないので詳細ページに遷移してしまう
data:これを書くと確認のダイアログをrailsが作ってくれる
code:rb
def destroy
task = Task.find(params:id) task.destroy
redirect_to tasks_url, notice: "タスク「#{task.name}」を削除しました。"
end